/**
 *    Copyright 2012 Geensoft S.A.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
package ar.com.greensoft.neo.web.jsf.beans.common;

import java.io.Serializable;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ar.com.greensoft.neo.dao.DaoException;
import ar.com.greensoft.neo.dao.GenericDao;
import ar.com.greensoft.neo.search.SearchInput;
import ar.com.greensoft.neo.web.jsf.converters.GenericConverter;
import ar.com.greensoft.neo.web.jsf.util.JSFMenuUtil;

/**
 * @author Jorge E. Villaverde
 * @version 1.0.0
 * @since 04/08/2011	
 */
public abstract class GenericEditFacadeBean<T, PK extends Serializable>  implements GenericEditFacade<T>, NavegationConstants, Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	protected static final Logger logger = LoggerFactory.getLogger(GenericEditFacadeBean.class);
	
	/**
	 * Facaded Entity 
	 */
	private T entity;
	
	/**
	 * <code>true</code> if editing an existing entity. <code>false</code> otherwise.
	 */
	private boolean editing = false;
	
	/**
	 * Current Displayed Page
	 */
    private int page = 1;
    
    /**
     * Cancel View
     */
    protected String cancelView;
    
    /**
     * Return View
     */
    protected String persistReturnView;
    	
	/**
	 * Search Input
	 */
	protected SearchInput input;
    
	@PostConstruct
	public void createBean() {
		editing = false;
		setEntity(newEntity());
	}
  	
	@Override
	public T getEntity() {
		return entity;
	}
	
	public void setEntity(T entity) {
		this.entity = entity;
	}
	
	@Override
	public List<T> getAllEntities(){
		GenericDao<T, PK> dao = getDao();
		if(dao == null)
			throw new java.lang.IllegalStateException("DAO not Injected");
        try {
			return dao.findAll();
		} catch (DaoException e) {
			addErrorMessage(e.getLocalizedMessage());
		}
        return null;
	}
	
	@Override
	public String remove() {
		beforeRemove();
		
		GenericDao<T, PK> dao = getDao();
		if(dao == null)
			throw new java.lang.IllegalStateException("DAO not Injected");
		if(logger.isTraceEnabled())
			logger.trace("Removing " + getClass().getSimpleName() + " with Id: " + getEntityId());
    	try {
			dao.remove(entity);
			afterRemove();
			
			return removeOk();
		} catch (DaoException e) {
			addErrorMessage(e.getLocalizedMessage());
	    	return removeError();
		}
	}
	
	public boolean isRemoveOk(){
		return true;
	}	

	@Override
	public String persist() {
		beforePersist();
		
    	GenericDao<T, PK> dao = getDao();
		if(dao == null)
			throw new java.lang.IllegalStateException("DAO not Injected");
		if(logger.isTraceEnabled())
			logger.trace("Persisting: " + entity);

		try{
			if(editing){
				beforeUpdate();
				dao.merge(entity);
				afterUpdate();
			}else{
				beforeCreate();
				dao.persist(entity);
				afterCreate();
			}
		}catch (Exception e) {
			addErrorMessage(e.getLocalizedMessage());
			return currentView();
		}	
		
		afterPersist();
		
		return persistOk();
    }
	
	@Override
    public void create(){
    	if(logger.isTraceEnabled())
    		logger.trace("Create");
		this.entity = newEntity();
	}

	@Override
	public void refresh(){
    	if(logger.isTraceEnabled())
    		logger.trace("Refreshing Entity: " + entity);
		try {
			setEntity(getDao().findById(getEntityId()));
		} catch (DaoException e) {
			addErrorMessage(e.getLocalizedMessage());
		}
	}
    
	
	/* Searcheable Bean */
	
	public SearchInput getInput() {
		return input;
	}
	
	public void search() {
		if(input == null)
			input = createSearchInput();
		input.setSearchActive(true);
	}
	
	public boolean isSearchActive() {
		if(input == null){
			this.input = createSearchInput();
		}
		return input.isSearchActive();
	}
	
	public int getPage() {
		return page;
	}

	public void setPage(int page) {
		this.page = page;
	}
		
	public String cancel() {
		return persistCancel();
	}

	protected String facadePath(String action){
		return JSFMenuUtil.buildMenuPath(userRolePath(), entityPrefix(), action);
	}
	
	public String showCreate() {
		createBean();
		updateCancelView();		
		return facadePath(createFileName());
	}

	public String showEdit() {
		if(entity == null)
			return facadePath(ERROR_ACTION);
		editing = true;
		updateCancelView();
		return facadePath(editFileName());
	}
	
	public String showRemove() {
		if(entity == null)
			return facadePath(ERROR_ACTION);
		editing = true;
		updateCancelView();
		return facadePath(removeFileName());
	}

	public boolean isEditing(){
		return editing;
	}
	
	protected void setEditing(boolean editing){
		this.editing = editing;
	}

	public String showSearch() {
		input = createSearchInput();
		beforeSearch();
		return facadePath(searchFileName());
	}

	public abstract GenericConverter<T, PK> getConverter();
	
	protected abstract GenericDao<T, PK> getDao();
	protected abstract T newEntity();
	protected abstract PK getEntityId();
	protected abstract String entityPrefix();
	protected abstract String userRolePath();
	protected abstract SearchInput createSearchInput();
	
	// Hook methods for Persistence

	protected void beforePersist(){	
	}
	protected void afterPersist(){
	}

	// Hook methods for Create

	protected void beforeCreate(){	
	}
	protected void afterCreate(){
	}

	// Hook methods for Update

	protected void beforeUpdate(){	
	}
	protected void afterUpdate(){
	}

	// Hook methods for Search
	protected void beforeSearch() {
	}

	// Hook methods for deletion
	
	protected void beforeRemove(){	
	}
	protected void afterRemove(){
	}
	
	protected String persistOk() {
		if(persistReturnView != null && !persistReturnView.isEmpty())
			return persistReturnView + JSFMenuUtil.JSF_REDIRECT;
		return showSearch();
	}
	 
	protected String persistCancel() {
		if(cancelView != null && !cancelView.isEmpty()) 
			return cancelView + JSFMenuUtil.JSF_REDIRECT;
		return showSearch();		
	}
	
	protected String persistError() {
		return facadePath(ERROR_ACTION);
	}

	protected String removeOk() {
		if(persistReturnView != null && !persistReturnView.isEmpty())
			return persistReturnView;
		return showSearch();
	}
	
	protected String removeError() {
		if(persistReturnView != null && !persistReturnView.isEmpty())
			return persistReturnView;
		return showSearch();		
	}
	
	protected String searchFileName() {
		return INDEX_ACTION;
	}

	protected String createFileName() {
		return FORM_ACTION;
	}
	protected String editFileName() {
		return FORM_ACTION;
	}
	protected String removeFileName() {
		return REMOVE_ACTION;
	}
	protected String revisionFileName(){
		return REVISION_DETAILS;
	}
	protected String revisionListFileName(){
		return REVISION_LIST;
	}
	
	protected void updateCancelView(){
		FacesContext ctx = FacesContext.getCurrentInstance();
		cancelView = ctx.getViewRoot().getViewId();
	}

	public String getCancelView() {
		return cancelView;
	}

	public void setCancelView(String cancelView) {
		this.cancelView = cancelView;
	}

	public String getPersistReturnView() {
		return persistReturnView;
	}

	public void setPersistReturnView(String persistReturnView) {
		this.persistReturnView = persistReturnView;
	}

	@Override
	public String showMenu() {
		return showSearch();
	}
	
	protected String currentView() {
		return FacesContext.getCurrentInstance().getViewRoot().getViewId();
	}

	protected void addErrorMessage(String localizedMessage) {
		FacesContext context = FacesContext.getCurrentInstance();
		FacesMessage message = new FacesMessage();
		message.setSeverity(FacesMessage.SEVERITY_ERROR);
		message.setSummary(localizedMessage);
		message.setDetail(localizedMessage);
		context.addMessage(null, message);
	}	
}